4. Procedimiento de ventana

4. Procedimiento de ventana

Cada ventana tiene una función asociada, esta función se conoce como procedimiento de ventana, y es la encargada de procesar adecuadamente todos los mensajes enviados a una determinada clase de ventana. Es la responsable de todo lo relativo al aspecto y al comportamiento de una ventana.

Normalmente, estas funciones están basadas en una estructura “switch” donde cada “case” corresponde aun determinado tipo de mensaje.

LRESULT CALLBACK WindowProcedure
(
   HWND hwnd, // Manipulador de ventana destinado del mensaje
   UINT msg,      // Código id del mensaje 
   WPARAM wParam, // Parámetro del mensaje tipo palabra 
   LPARAM lParam  // Parámetro del mensaje tipo doble 
  );
LRESULT CALLBACK WindowProcedure
(
   HWND hwnd, // Manipulador de ventana destinado del mensaje
   UINT msg,      // Código id del mensaje 
   WPARAM wParam, // Parámetro del mensaje tipo palabra 
   LPARAM lParam  // Parámetro del mensaje tipo doble 
  );

Todas las funciones de ventana han de tener los mismos valor de retorno y parámetros.

El miembro lpfnWndProc de la estructura WNDCLASS es un puntero a una función de este tipo, esa función es la que se encargará de procesar todos los mensajes para esa clase de ventana. Cuando registremos nuestra clase de ventana, tendremos que asignar a ese miembro el puntero a nuestro procedimiento de ventana.

Para más detalles sobre la función de procedimiento de ventana, consultar WindowProc.

4.1. Implementación de procedimiento de ventana simple

/* Esta función es llamada por la función del API DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)     // manipulador del mensaje
    {
        case WM_DESTROY:
           PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes 
           break;
        default: // para los mensajes de los que no nos ocupamos 
           return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}
/* Esta función es llamada por la función del API DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)     // manipulador del mensaje
    {
        case WM_DESTROY:
           PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes 
           break;
        default: // para los mensajes de los que no nos ocupamos 
           return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

En general, habrá tantos procedimientos de ventana como programas diferentes y todos serán distintos, pero también tendrán algo en común: todos ellos procesarán los mensajes que lleguen a una clase de ventana.

En este ejemplo sólo procesamos un tipo de mensaje, se trata de WM_DESTROY que es el mensaje que se envía a una ventana cuando se recibe un comando de cerrar, ya sea por menú o mediante el icono del aspa en la esquina superior derecha de la ventana.

Este mensaje sólo indica que el usuario tiene la intención de abandonar la aplicación, para cerrar ficheros, liberar memoria, guardar variables, etc, o incluso para cancelar la salida de la aplicación.

En el caso del ejemplo, efectivamente cierra la aplicación, y lo hace enviándole un mensaje WM_QUIT, mediante la función PostQuitMessage.

El resto de los mensajes se procesan en el caso “default”, y simplemente se cede su tratamiento a la función del API que hace el proceso por defecto para cada mensaje, DefWindowProc. Este es el camino que sigue el mensaje WM_QUIT cuando llega, ya que el proceso por defecto para este mensaje es cerrar la aplicación.

En posteriores capítulos veremos como se complica paulatinamente esta función, añadiendo más y más mensajes.

4.2. CÓDIGO DE EJEMPLO

#include <windows.h>

// http://winapi.conclase.net/curso/index.php?cap=004#inicio

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); //  Prototipo del procedimiento de ventana

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)  // función principal WinMain
{
    HWND        hwnd;         // Manipulador de ventana
    MSG         mensaje;      // Mensajes recibidos por la aplicación
    WNDCLASSEX  wincl;        // Estructura de datos para la clase de ventana

    /* Estructura de ventana */
    wincl.hInstance     = hThisInstance;
    wincl.lpszClassName = "NUESTRA_CLASE";
    wincl.lpfnWndProc   = WindowProcedure;      // Función invocada por Windows
    wincl.style         = CS_DBLCLKS;           // Captura los doble-clicks
    wincl.cbSize        = sizeof (WNDCLASSEX);

    /* Usar icono y puntero por defecto */
    wincl.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm       = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor       = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName  = NULL;   // Sin menú
    wincl.cbClsExtra    = 0;      // Sin información adicional para la clase
    wincl.cbWndExtra    = 0;      // Sin información adicional para la ventana
    wincl.hbrBackground = GetSysColorBrush(COLOR_BACKGROUND); // Color de fondo por defecto para la ventana


    /* Registrar la clase de ventana */
    if (!RegisterClassEx(&wincl))
    {
        return 0; // si falla, sale del programa
    }

    /* Crear la ventana */
    hwnd = CreateWindowEx(0,     // se puede moficiar
            "NUESTRA_CLASE",     // Nombre de la clase
            "Ejemplo 001",       // Texto del título
            WS_OVERLAPPEDWINDOW, // Tipo por defecto
            CW_USEDEFAULT,       // Posición X de la ventana, por defecto
            CW_USEDEFAULT,       // Posición Y de la ventana, por defecto
            544,                 // Ancho
            375,                 // Alto en pixels
            HWND_DESKTOP,        // La ventana es hija del escritorio
            NULL,                // Sin menú
            hThisInstance,       // Manipulador de instancia
            NULL  );             // No hay datos de creación de ventana


    ShowWindow(hwnd, SW_SHOWDEFAULT);  // Mostrar la ventana

    /* Bucle de mensajes, se ejecuta hasta que haya error o GetMessage devuelva FALSE */
    while(TRUE == GetMessage(&mensaje, NULL, 0, 0))
    {
        TranslateMessage(&mensaje); // Traducir mensajes de teclas virtuales a mensajes de caracteres
        DispatchMessage(&mensaje);  // Enviar mensaje al procedimiento de ventana
    }

    return mensaje.wParam;  // Salir con valor de retorno
}


/*  Esta función es invocada por la función DispatchMessage()  */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam)
{
    switch (mensaje)                  /* manipulador de mensaje */
    {
        case WM_DESTROY:
            PostQuitMessage (0);       /* Envía el mensaje WM_QUIT a la cola de mensajes */
            break;
        default:                      /* Mensajes que no queremos manejar */
            return DefWindowProc (hwnd, mensaje, wParam, lParam);
    }

    return 0;
}
#include <windows.h>

// http://winapi.conclase.net/curso/index.php?cap=004#inicio

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); //  Prototipo del procedimiento de ventana

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)  // función principal WinMain
{
    HWND        hwnd;         // Manipulador de ventana
    MSG         mensaje;      // Mensajes recibidos por la aplicación
    WNDCLASSEX  wincl;        // Estructura de datos para la clase de ventana

    /* Estructura de ventana */
    wincl.hInstance     = hThisInstance;
    wincl.lpszClassName = "NUESTRA_CLASE";
    wincl.lpfnWndProc   = WindowProcedure;      // Función invocada por Windows
    wincl.style         = CS_DBLCLKS;           // Captura los doble-clicks
    wincl.cbSize        = sizeof (WNDCLASSEX);

    /* Usar icono y puntero por defecto */
    wincl.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm       = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor       = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName  = NULL;   // Sin menú
    wincl.cbClsExtra    = 0;      // Sin información adicional para la clase
    wincl.cbWndExtra    = 0;      // Sin información adicional para la ventana
    wincl.hbrBackground = GetSysColorBrush(COLOR_BACKGROUND); // Color de fondo por defecto para la ventana


    /* Registrar la clase de ventana */
    if (!RegisterClassEx(&wincl))
    {
        return 0; // si falla, sale del programa
    }

    /* Crear la ventana */
    hwnd = CreateWindowEx(0,     // se puede moficiar
            "NUESTRA_CLASE",     // Nombre de la clase
            "Ejemplo 001",       // Texto del título
            WS_OVERLAPPEDWINDOW, // Tipo por defecto
            CW_USEDEFAULT,       // Posición X de la ventana, por defecto
            CW_USEDEFAULT,       // Posición Y de la ventana, por defecto
            544,                 // Ancho
            375,                 // Alto en pixels
            HWND_DESKTOP,        // La ventana es hija del escritorio
            NULL,                // Sin menú
            hThisInstance,       // Manipulador de instancia
            NULL  );             // No hay datos de creación de ventana


    ShowWindow(hwnd, SW_SHOWDEFAULT);  // Mostrar la ventana

    /* Bucle de mensajes, se ejecuta hasta que haya error o GetMessage devuelva FALSE */
    while(TRUE == GetMessage(&mensaje, NULL, 0, 0))
    {
        TranslateMessage(&mensaje); // Traducir mensajes de teclas virtuales a mensajes de caracteres
        DispatchMessage(&mensaje);  // Enviar mensaje al procedimiento de ventana
    }

    return mensaje.wParam;  // Salir con valor de retorno
}


/*  Esta función es invocada por la función DispatchMessage()  */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam)
{
    switch (mensaje)                  /* manipulador de mensaje */
    {
        case WM_DESTROY:
            PostQuitMessage (0);       /* Envía el mensaje WM_QUIT a la cola de mensajes */
            break;
        default:                      /* Mensajes que no queremos manejar */
            return DefWindowProc (hwnd, mensaje, wParam, lParam);
    }

    return 0;
}